use std::marker::PhantomData;
use super::*;

#[derive(Debug)]
pub struct NoneError;
impl std::error::Error for NoneError { }
impl std::fmt::Display for NoneError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "")
    }
}

pub struct NoneExecutor;

impl<I:Send + Sync + 'static> RayonPipeline<I> for NoneExecutor {
    type Error = NoneError;
    fn exec<'scope>(
        &'scope self, 
        _scope:&rayon::Scope<'scope>,
        _input:I,
    ) -> Result<(), Self::Error> {
        Ok(())
    }
}

pub struct SendExecutor<T>(std::sync::mpsc::Sender<T>);
pub struct ReceiverWrapper<T>(std::sync::mpsc::Receiver<T>);

impl<T:Send + Sync + 'static> SendExecutor<T> {
    pub fn channel() -> (Self, ReceiverWrapper<T>) {
        let (rx, tx) = std::sync::mpsc::channel();
        (Self(rx), ReceiverWrapper(tx))
    }
}

impl<T> IntoIterator for ReceiverWrapper<T> {
    type IntoIter = <std::sync::mpsc::Receiver::<T> as IntoIterator>::IntoIter;
    type Item = <std::sync::mpsc::Receiver::<T> as IntoIterator>::Item;
    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

pub struct RayonImmutPhantom;
impl<I, O> ImmutExecutor<I, O> for RayonImmutPhantom 
    where 
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
{
    type Error = NoneError;
    fn exec<'scope, F:FnMut(O)>(&'scope self, _input:I, _output:F) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<I:Send + Sync + 'static> RayonPipeline<I> for SendExecutor<I> {
    type Error = std::sync::mpsc::SendError<I>;
    fn exec<'scope>(
        &'scope self, 
        _scope:&rayon::Scope<'scope>,
        input:I,
    ) -> Result<(), Self::Error> {
        self.0.send(input)
    }
}


pub struct RayonMutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:MutExecutor<I, O>,
{
    _unused:PhantomData<(I, O)>,
    inner:E,
}

impl<I, O, E> Clone for RayonMutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:MutExecutor<I, O> + Clone,
{   
    fn clone(&self) -> Self {
        Self { _unused: PhantomData, inner: self.inner.clone() }
    }
}

impl<I, O, E> RayonMutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:MutExecutor<I, O>,
{
    pub fn exec<'scope, NE>(
        &mut self, 
        scope:&rayon::Scope<'scope>, 
        input:I, 
        next_executor:&'scope NE
    ) -> Result<(), E::Error>
    where NE:RayonPipeline<O>
    {
        self.inner.exec(input, |output|{
            next_executor.exec(scope, output).expect("exec error");
        })
    }
}

impl<I, O, E> From<E> for RayonMutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:MutExecutor<I, O>,
{
    fn from(value: E) -> Self {
        Self { _unused: PhantomData, inner: value }
    }
}


pub struct RayonImmutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:ImmutExecutor<I, O>,
{
    _unused:PhantomData<(I, O)>,
    inner:E,
}

impl<I, O, E> Clone for RayonImmutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:ImmutExecutor<I, O> + Clone,
{   
    fn clone(&self) -> Self {
        Self { _unused: PhantomData, inner: self.inner.clone() }
    }
}

impl<I, O, E> RayonImmutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:ImmutExecutor<I, O>,
{
    pub fn exec<'scope, NE>(
        &'scope self, 
        scope:&rayon::Scope<'scope>, 
        input:I, 
        next_executor:&'scope NE
    ) -> Result<(), E::Error>
    where NE:RayonPipeline<O> {
        scope.spawn(|scope|{
            self.inner.exec(input, |output|{
                next_executor.exec(scope, output).expect("exec error");
            }).expect("exec error");
        });
        Ok(())
    }
}

impl<I, O, E> From<E> for RayonImmutWrapper<I, O, E>
    where
        I:Send + Sync + 'static,
        O:Send + Sync + 'static,
        E:ImmutExecutor<I, O>,
{
    fn from(value: E) -> Self {
        Self { _unused: PhantomData, inner: value }
    }
}

#[test]
fn test_exe_size() {
    use std::sync::Arc;
    use crate::backup::dedup::hasher::*;
    use crate::common::chunking::*;
    println!("Arc<ChunkingSelectot>:{}", std::mem::size_of::<Arc<ChunkingSelector>>());
    println!("ChunkingSelectot:{}", std::mem::size_of::<ChunkingSelector>());
    println!("Arc<ChunkHasher>:{}", std::mem::size_of::<Arc<ChunkHasher>>());
    println!("ChunkHasher:{}", std::mem::size_of::<ChunkHasher>());
}